/**
 * 
 */
package com.googlecode.pcapj;

import java.io.IOException;

import org.bridj.Pointer;

import com.googlecode.pcapj.bridj.Wpcap;
import com.googlecode.pcapj.bridj.Wpcap.packet_handler;
import com.googlecode.pcapj.bridj.bpf_program;
import com.googlecode.pcapj.bridj.pcap_pkthdr;

/**
 * 
 */
class PcapWrapper {
	private Pointer<?> pointer;
	private Pointer<Byte> errorBuffer;
	private Integer dataLinkLayer;
	
	private void requireClosed() throws IOException{
		if(!isClosed()){
			throw new IOException("already opened.");
		}
	}
	private void requireOpened() throws IOException{
		if(isClosed()){
			throw new IOException("not opened.");
		}
	}
	
	PcapWrapper(Pointer<Byte> errorBuffer) {
		super();
		this.errorBuffer = errorBuffer;
	}

	Pointer<?> getPointer() {
		return pointer;
	}
	void open(Pointer<Byte> sourceName, int snapLength, int flags, int readTimeout, Pointer<?> auth) throws IOException{
		requireClosed();
		Pointer<Byte> errorBuffer = this.errorBuffer;
		try{
			Pointer<?> pointer = Wpcap.pcap_open(sourceName, snapLength, flags, readTimeout, auth, errorBuffer);
			if(pointer == Pointer.NULL){
				throw new IOException(errorBuffer.getCString());
			}
			this.pointer = pointer;
			
		}finally{
			errorBuffer.release();
		}
	}
	
	boolean loop(packet_handler packet_handler, int count, Pointer<Byte> user) throws IOException{
		requireOpened();
		Pointer<Wpcap.packet_handler> callback = Pointer.pointerTo(packet_handler);
		try{
			int result = Wpcap.pcap_loop(pointer, count, callback, user);
			if(result == -1){
				Pointer<Byte> errorBuffer = Wpcap.pcap_geterr(pointer);
				throw new IOException(errorBuffer.getCString());
			}
			if(result < -2){
				throw new PcapJRuntimeException("pcap_loop returned unexpected value(" + result + ")");
			}
			return result == -2;
			
		}finally{
			callback.release();
		}
	}
	
	void breakLoop() throws IOException {
		requireOpened();
		Wpcap.pcap_breakloop(pointer);
	}
	/**
	 * @return true if EOF
	 */
	boolean read(Pointer<Pointer<pcap_pkthdr>> pkt_header, Pointer<Pointer<Byte>> pkt_data)throws IOException{
		requireOpened();
    	
		int result = Wpcap.pcap_next_ex(pointer, pkt_header, pkt_data);
		if(result == 0){
			throw new PcapJTimeoutException("packet read timeout");
		}
		if(result == -1){
			Pointer<Byte> errorBuffer = Wpcap.pcap_geterr(pointer);
			throw new IOException(errorBuffer.getCString());
		}
		return result == -2;
	}
	
	void setFilter(String filterString, int netmask) throws IOException {
		requireOpened();
		Pointer<bpf_program> fcode = Pointer.allocate(bpf_program.class);
		Pointer<Byte> filter = Pointer.pointerToCString(filterString);
		try{
			// compile the filter
			if(Wpcap.pcap_compile(pointer, fcode, filter, 1, netmask) < 0){
				Pointer<Byte> errorBuffer = Wpcap.pcap_geterr(pointer);
				throw new IOException(errorBuffer.getCString());
			}
			// Sets the filter
			if(Wpcap.pcap_setfilter(pointer, fcode) < 0){
				Pointer<Byte> errorBuffer = Wpcap.pcap_geterr(pointer);
				throw new IOException(errorBuffer.getCString());
			}
			
		}finally{
			fcode.release();
			filter.release();
		}
	}
	
	Pointer<?> openDumpFile(Pointer<Byte> fileName) throws IOException {
		requireOpened();
		Pointer<?> dumpFilePointer = Wpcap.pcap_dump_open(pointer, fileName);
	    if(dumpFilePointer == Pointer.NULL)
	    {
			Pointer<Byte> errorBuffer = Wpcap.pcap_geterr(pointer);
			throw new IOException(errorBuffer.getCString());
	    }
		return dumpFilePointer;
	}
	void writeDumpFile(Pointer<?> pcap_dumper, Pointer<pcap_pkthdr> h, Pointer<Byte> sp) throws IOException{
		requireOpened();
		Wpcap.pcap_dump(pcap_dumper, h, sp);
		
	}
	int getDatalink() throws IOException{
		requireOpened();
		if(dataLinkLayer == null){
			dataLinkLayer = Wpcap.pcap_datalink(pointer); 
		}
		return dataLinkLayer;
	}
	public void close(){
		if(!isClosed()){
			try{
				Wpcap.pcap_close(pointer);
				
			}finally{
				pointer = Pointer.NULL;
				
			}
		}
	}
	/* (non-Javadoc)
	 * @see com.googlecode.pcapj.PacketCapture#isClosed()
	 */
	public boolean isClosed() {
		return pointer == Pointer.NULL;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#finalize()
	 */
	@Override
	protected void finalize() throws Throwable {
		close();
		super.finalize();
	}
	
}
